package cn.wen233.core.service.impl;

import cn.wen233.basic.exceptions.BaseException;
import cn.wen233.basic.model.Instance;
import cn.wen233.basic.model.NodeRecord;
import cn.wen233.basic.model.Service;
import cn.wen233.basic.utils.StringUtils;
import cn.wen233.core.filter.InstanceFilter;
import cn.wen233.core.mapper.InstanceMapper;
import cn.wen233.core.page.PageOut;
import cn.wen233.core.service.InstanceService;
import cn.wen233.core.service.NodeRecordService;
import cn.wen233.core.wrapper.query.ServiceQuery;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * 数据库实例service实现
 *
 * @author wenei
 * @date 2021-10-23 10:19
 */
@org.springframework.stereotype.Service
@Slf4j
@RequiredArgsConstructor
public class PersistenceInstanceServiceImpl implements InstanceService, InitializingBean {

    @Resource(type = PersistenceNodeRecordServiceImpl.class)
    private NodeRecordService nodeRecordService;

    /**
     * 超时时间
     */
    private static final Long TIMEOUT = 10000L;

    /**
     * 过期删除时间
     */
    private static final Long DELETE_TIME = 30000L;

    /**
     * 节点是否有效检查周期
     */
    private static final Long RATE = 5000L;

    private final ScheduledThreadPoolExecutor SINGLE_THREAD_POOL =
            new ScheduledThreadPoolExecutor(1, r -> {
                Thread thread = new Thread(r);
                thread.setName("instance-heartbeat-thread");
                return thread;
            });

    private final Map<String, Long> instanceLastTimeMap = new HashMap<>();

    private final InstanceMapper instanceMapper;

    @Override
    public Instance findById(String id) {
        return Optional.ofNullable(instanceMapper.selectInstanceById(id)).orElseThrow(() -> new BaseException("未找到id={%s}的Instance", id));
    }

    @Override
    public void heartbeat(String instanceId) {
        instanceLastTimeMap.put(instanceId, System.currentTimeMillis());
    }

    @Override
    public PageOut<Instance> findPage(InstanceFilter filter) {
        List<Instance> allInstance = instanceMapper.selectAllInstanceByServiceId(null);
        List<Instance> instanceList = allInstance.stream()
                .filter((instance -> {
                    boolean flag = true;
                    if (!StringUtils.isEmpty(filter.getServiceId())) {
                        flag = instance.getServiceId().startsWith(filter.getServiceId());
                    }
                    if (!StringUtils.isEmpty(filter.getServiceNameLike())) {
                        flag = instance.getServiceName().contains(filter.getServiceNameLike());
                    }
                    return flag;
                }))
                .skip((long) filter.getSize() * filter.getNumber())
                .limit(filter.getSize())
                .collect(Collectors.toList());
        return PageOut.of(instanceList, allInstance.size(), filter.getSize(), filter.getNumber());
    }

    @Override
    public List<Instance> getServiceInstance(String namespaceId, String serviceId) {
        Service service = ServiceQuery.getNamespaceService(namespaceId, serviceId);
        if (service == null) {
            throw new BaseException("未找到指定Service");
        }
        return instanceMapper.selectAllInstanceByServiceId(service.getId());
    }

    @Override
    public Instance registerInstance(String namespaceId, String serviceId, String host, int port) {
        Service service = ServiceQuery.getNamespaceService(namespaceId, serviceId);

        // 节点注册记录
        NodeRecord nodeRecord = new NodeRecord();
        nodeRecord.setServiceId(serviceId);
        nodeRecord.setAppName(service.getAppName());
        nodeRecord.setIp(host);
        nodeRecord.setPort(port);
        nodeRecord.setCreateTime(System.currentTimeMillis());
        nodeRecordService.insert(nodeRecord);

        List<Instance> serviceInstanceList = getServiceInstance(namespaceId, serviceId);
        Instance currentInstance = serviceInstanceList.stream().filter(instance -> {
            return instance.getIp().equals(host) && instance.getPort() == port;
        }).findFirst().orElse(null);
        if (currentInstance != null) {
            log.warn("节点已经存在 service={}, host={}, port={}", service, host, port);
            return currentInstance;
        }
        Instance model = new Instance();
//        model.setInstanceId();
        model.setIp(host);
        model.setPort(port);

        model.setServiceName(service.getName());

        model.setServiceId(service.getId());
        model.setStatus(0);
        model.setEnabled(true);
        instanceMapper.insertInstance(model);
        // 放入当前时间
        instanceLastTimeMap.put(model.getId(), System.currentTimeMillis());
        return model;
    }

    @Override
    public void removeInstance(String namespaceId, String serviceId, String id) {
        ServiceQuery.getNamespaceService(namespaceId, serviceId);
        findById(id);
        instanceMapper.deleteInstance(id);
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        log.info("检查instance心跳任务启动");
        SINGLE_THREAD_POOL.scheduleAtFixedRate(() -> {
            try {
                List<Instance> allInstance = instanceMapper.selectAllInstanceByServiceId(null);
                if (allInstance == null || allInstance.size() == 0) {
                    return;
                }
                Long nowTime = System.currentTimeMillis();
                List<String> todoDeleteList = new ArrayList<>();
                List<String> validList = new ArrayList<>();
                List<String> invalidList = new ArrayList<>();
                allInstance.forEach(instance -> {
                    String id = instance.getId();
                    Long lastTime = instanceLastTimeMap.get(id);
                    // 没有最近时间的直接删除
                    if (lastTime == null) {
                        todoDeleteList.add(id);
                        return;
                    }
                    // 最近时间超过删除限制时间
                    if (nowTime - lastTime > DELETE_TIME) {
                        todoDeleteList.add(id);
                        return;
                    }
                    // 最近时间超过超时时间
                    if (nowTime - lastTime > TIMEOUT) {
                        invalidList.add(id);
                        return;
                    }
                    validList.add(id);
                });
                if (!CollectionUtils.isEmpty(todoDeleteList)) {
                    log.info("失效instanceIdList={}", todoDeleteList);
                    instanceMapper.batchDeleteInstance(todoDeleteList);
                }
                if (!CollectionUtils.isEmpty(validList)) {
                    instanceMapper.batchUpdateInstanceStatus(validList, 0);
                }
                if (!CollectionUtils.isEmpty(invalidList)) {
                    instanceMapper.batchUpdateInstanceStatus(invalidList, 1);
                }
            } catch (Exception e) {
                log.error("定时任务出现异常", e);
            }
        }, RATE * 2, RATE, TimeUnit.MILLISECONDS);
    }
}
